#include "TimeUtil.h"
#include <limits>
#include "macro.h"

using namespace std;


#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif


MRealtimeClockType g_launchTime;
MMonotonicClockType g_launchMonotonicTime;

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
LARGE_INTEGER g_monotonicTimerFreq;
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
mach_timebase_info_data_t g_monotonicTimerFreq;
#else
#endif


GlobalFlag initTimeUtil() {
    GlobalFlag ret;
    if ((ret = getRealtimeClock(g_launchTime)) != GlobalFlag_Success) {
        return ret;
    }
    if ((ret = getMonotonicClock(g_launchMonotonicTime)) != GlobalFlag_Success) {
        return ret;
    }
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    if (!QueryPerformanceFrequency(&g_monotonicTimerFreq)) {
        return GlobalFlag_CommonError;
    }
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    if (mach_timebase_info(&g_monotonicTimerFreq) != KERN_SUCCESS) {
        return GlobalFlag_CommonError;
    }
#else
#endif
    return GlobalFlag_Success;
}


//各种内部函数
namespace {
    struct IntermediateFormat {
        int day; // 自0000-03-01起的天数
        int min;
        int sec;
        int nsec;
    };

    //输入限制在[-1400000-03-01, +1400000-03-01)之间
    GlobalFlag parseRealtimeClock(const MRealtimeClockType & t, IntermediateFormat & out) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
        if (t.dwHighDateTime >= 0x80000000U) {
            return GlobalFlag_BadArgument;
        }
        ULARGE_INTEGER tt;
        tt.HighPart = t.dwHighDateTime;
        tt.LowPart = t.dwLowDateTime;
        ULONGLONG ts = tt.QuadPart / 10000000ULL;
        out.nsec = static_cast<unsigned>(tt.QuadPart % 10000000ULL) * 100;
        out.day = static_cast<int>(ts / 86400ULL) + 584694;
        unsigned s = static_cast<unsigned>(ts % 86400ULL);
#else
# if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        if (t.tv_usec < 0 || t.tv_usec >= 1000000) {
            return GlobalFlag_BadArgument;
        }
        out.nsec = t.tv_usec * 1000;
# else
        if (t.tv_nsec < 0 || t.tv_nsec >= 1000000000) {
            return GlobalFlag_BadArgument;
        }
        out.nsec = t.tv_nsec;
# endif
        //为什么要这么写呢，因为time_t可以是signed，也可以是unsigned
        if (!(t.tv_sec < INT64_C(44117570764800)) || (t.tv_sec < 0 && t.tv_sec < INT64_C(-44241894835200))) {
            return GlobalFlag_BadArgument;
        }
        unsigned s;
        if (t.tv_sec >= 0) {
            out.day = static_cast<int>(t.tv_sec / 86400) + 719468;
            s = t.tv_sec % 86400;
        } else {
            out.day = static_cast<int>((t.tv_sec + 1) / 86400) + 719467;
            s = (t.tv_sec + 1) % 86400 + 86399;
        }
#endif
        out.min = s / 60U;
        out.sec = s % 60U;
        return GlobalFlag_Success;
    }

    void convertToISO8601(const IntermediateFormat & in, ISOTime & out) {
        int d4 = in.day * 4 + 3;
        int c;
        unsigned c4r;
        if (in.day >= 0) {
            out.wday = (in.day + 2) % 7 + 1;
            c = d4 / 146097;
            c4r = d4 % 146097;
        } else {
            out.wday = (in.day - 4) % 7 + 7;
            c = (d4 + 1) / 146097 - 1;
            c4r = (d4 + 1) % 146097 + 146096;
        }
        unsigned cr4 = c4r | 3U;
        unsigned y = cr4 / 1461U;
        unsigned yr = cr4 % 1461U / 4U;
        unsigned yr5 = yr * 5U + 614U;
        unsigned m = yr5 / 153U;
        unsigned mr = yr5 % 153U / 5U;
        int year = c * 100 + static_cast<int>(y);
        int leap = (y % 4U == 0) && (y != 0 || c % 4U == 0);
        if (m >= 14U) {
            out.year = year + 1;
            out.month = m - 13U;
            out.yday = yr - 305U;
        } else {
            out.year = year;
            out.month = m - 1U;
            out.yday = yr + 60U + leap;
        }
        out.day = mr + 1U;
        if (static_cast<int>(yr) - out.wday >= 302) {
            out.wyear = year + 1;
            out.week = (yr - out.wday - 295U) / 7U;
        } else {
            out.wyear = year;
            out.week = (yr - out.wday + 70U + leap) / 7U;
        }
        out.hour = in.min / 60U;
        out.min = in.min % 60U;
        out.sec = in.sec;
        out.nsec = in.nsec;
    }

	int getDays(int year, int yday) {
		year--;
		if (year >= 0) {
			int c = year / 100;
			return year * 365 + year / 4 - c + c / 4 + yday + 305;
		} else {
			int c = (year + 1) / 100 - 1;
			return year * 365 + (year + 1) / 4 - c + (c + 1) / 4 + yday + 303;
		}
	}
}


GlobalFlag extractRealtimeToUTC(const MRealtimeClockType & t, ISOTime & dateTime) {
    GlobalFlag ret;
    //计算UTC天数+分钟数+秒数+纳秒数
    IntermediateFormat temp;
    if ((ret = parseRealtimeClock(t, temp)) != GlobalFlag_Success) {
		memset(&dateTime, 0, sizeof(dateTime));
        return ret;
    }
    //换算时区，这里是UTC所以什么都不做
    dateTime.tzmin = 0;
    //计算年月日等
    convertToISO8601(temp, dateTime);
    return GlobalFlag_Success;
}


GlobalFlag extractRealtimeToLocalTime(const MRealtimeClockType & t, ISOTime & dateTime) {
    GlobalFlag ret;
    //计算UTC天数+分钟数+秒数+纳秒数
    IntermediateFormat utc;
    if ((ret = parseRealtimeClock(t, utc)) != GlobalFlag_Success) {
		memset(&dateTime, 0, sizeof(dateTime));
        return ret;
    }
    //换算时区，因为C++并不直接提供所以……
	long long unixTime = (utc.day - 719468) * 86400LL + (utc.min * 60 + utc.sec);
	if (unixTime > numeric_limits<time_t>::max() || unixTime < numeric_limits<time_t>::min()) {
		memset(&dateTime, 0, sizeof(dateTime));
		return GlobalFlag_BadArgument;
	}
	time_t ti = static_cast<time_t>(unixTime);
	struct tm * _tm = localtime(&ti);
	if (_tm == nullptr) {
		memset(&dateTime, 0, sizeof(dateTime));
		return GlobalFlag_BadArgument;
	}
	IntermediateFormat local;
	local.day = getDays(_tm->tm_year + 1900, _tm->tm_yday + 1);
	local.min = _tm->tm_hour * 60 + _tm->tm_min;
	local.sec = _tm->tm_sec;
	local.nsec = utc.nsec;
    dateTime.tzmin = (local.day - utc.day) * 1440 + (local.min - utc.min);
    //计算年月日等
    convertToISO8601(local, dateTime);
    return GlobalFlag_Success;
}


void convertToTm(const ISOTime & dateTime, struct tm & tms) {
	tms.tm_sec = dateTime.sec;
	tms.tm_min = dateTime.min;
	tms.tm_hour = dateTime.hour;
	tms.tm_mday = dateTime.day;
	tms.tm_mon = dateTime.month - 1;
	tms.tm_year = dateTime.year - 1900;
	tms.tm_wday = dateTime.wday == 7 ? 0 : dateTime.wday;
	tms.tm_yday = dateTime.yday - 1;
	tms.tm_isdst = -1;
}
